`timescale 1ns / 1ps

`include "defines.vh"

module myCPU (
    input  wire         cpu_rst,
    input  wire         cpu_clk,

    // Interface to IROM
    output wire [15:0]  inst_addr,
    input  wire [31:0]  inst,
    
    // Interface to Bridge
    output wire [31:0]  Bus_addr,
    input  wire [31:0]  Bus_rdata,
    output wire         Bus_wen,
    output wire [31:0]  Bus_wdata

`ifdef RUN_TRACE
    ,// Debug Interface
    output wire         debug_wb_have_inst,
    output wire [31:0]  debug_wb_pc,
    output              debug_wb_ena,
    output wire [ 4:0]  debug_wb_reg,
    output wire [31:0]  debug_wb_value
`endif
);

// TODO: 完成你自己的流水线CPU设计

	
// ifetch wires
wire [31:0] pc4;

// idecode wires
wire [1:0] npc_op;
wire [2:0] sext_op;
wire rf_we;
wire [1:0] rf_wsel;
wire alub_sel;
wire [3:0] alu_op;
wire ram_we;
wire [31:0] ext;
wire [31:0] rD1;
wire [31:0] rD2;
wire [31:0] wD;
wire [1:0] ID_rRe;

// execute wires
wire f;
wire [31:0] C;

/*
pipeline wires
*/
// IF/ID wires
wire [31:0] inst_temp;
wire [31:0] pc4_temp_ID;

// ID/EXE wires
wire [1:0] npc_op_temp;
wire rf_we_temp_EXE;
wire [1:0] rf_wsel_temp_EXE;
wire ram_we_temp_EXE;
wire alub_sel_temp;
wire [3:0] alu_op_temp;
wire [4:0] wR_temp_EXE;
wire [31:0] pc4_temp_EXE;
wire [31:0] rD1_temp;
wire [31:0] rD2_temp_EXE;
wire [31:0] ext_temp_EXE;

// EXE/MEM wires
wire rf_we_temp_MEM;
wire [1:0] rf_wsel_temp_MEM;
wire [4:0] wR_temp_MEM;
wire [31:0] ram_we_temp_MEM;
wire [31:0] pc4_temp_MEM;
wire [31:0] C_temp_MEM;
wire [31:0] rD2_temp_MEM;
wire [31:0] ext_temp_MEM;

// MEM/WB wires
wire rf_we_temp_WB;
wire [1:0] rf_wsel_temp_WB;
wire [4:0] wR_temp_WB;
wire [31:0] pc4_temp_WB;
wire [31:0] rdo_temp;
wire [31:0] C_temp_WB;
wire [31:0] ext_temp_WB;

// data hazard detector wires
wire [4:0] suspend;

// control hazard detector wires
wire flush;

`ifdef RUN_TRACE
	wire [31:0] pc_IF = inst_addr;
	wire [31:0] pc_ID = pc4_temp_ID - 4;
	wire [31:0] pc_EXE = pc4_temp_EXE - 4;
	wire [31:0] pc_MEM = pc4_temp_MEM - 4;
	wire [31:0] pc_WB = pc4_temp_WB - 4;
`endif

ifetch ifetch (
	.rst(cpu_rst),
	.clk(cpu_clk),
	.susp({suspend[4], suspend[3]}),
	.br(f),
	.op(npc_op_temp),
	.offset(ext_temp_EXE),
	.radr(rD1_temp),
	.pc4_br(pc4_temp_EXE),
	.pc4(pc4),
	.pc(inst_addr)
);

IF_ID IF_ID(
	.clk(cpu_clk),
	.rst(cpu_rst),
	.flush(flush),
	.susp({suspend[4], suspend[2]}),
	.inst_pre(inst),
	.pc4_pre(pc4),
	.inst_next(inst_temp),
	.pc4_next(pc4_temp_ID)
);

control control (
	.funct3(inst_temp[14:12]),
	.funct7(inst_temp[31:25]),
	.opcode(inst_temp[6:0]),
	.npc_op(npc_op),
	.sext_op(sext_op),
	.rf_we(rf_we),
	.rf_wsel(rf_wsel),
	.alub_sel(alub_sel),
	.alu_op(alu_op),
	.ram_we(ram_we),
	.rRe(ID_rRe)
);

idecode idecode (
	.rst(cpu_rst),
	.clk(cpu_clk),
	.we(rf_we_temp_WB),
	.sext_op(sext_op),
	.wR(wR_temp_WB),
	.inst(inst_temp[31:7]),
	.wD(wD),
	.ext(ext),
	.rD1(rD1),
	.rD2(rD2)
);

ID_EXE ID_EXE(
	.clk(cpu_clk),
	.rst(cpu_rst),
	.flush(flush),
	.susp({suspend[4], suspend[1]}),
	.npc_op_pre(npc_op),
	.rf_we_pre(rf_we),
	.rf_wsel_pre(rf_wsel),
	.ram_we_pre(ram_we),
	.alu_op_pre(alu_op),
	.alub_sel_pre(alub_sel),
	.pc4_pre(pc4_temp_ID),
	.ext_pre(ext),
	.rD1_pre(rD1),
	.rD2_pre(rD2),
	.wR_pre(inst_temp[11:7]),
	.npc_op_next(npc_op_temp),
	.rf_we_next(rf_we_temp_EXE),
	.rf_wsel_next(rf_wsel_temp_EXE),
	.ram_we_next(ram_we_temp_EXE),
	.alu_op_next(alu_op_temp),
	.alub_sel_next(alub_sel_temp),
	.pc4_next(pc4_temp_EXE),
	.ext_next(ext_temp_EXE),
	.rD1_next(rD1_temp),
	.rD2_next(rD2_temp_EXE),
	.wR_next(wR_temp_EXE)
);

execute execute (
	.alub_sel(alub_sel_temp),
	.alu_op(alu_op_temp),
	.rD1(rD1_temp),
	.rD2(rD2_temp_EXE),
	.ext(ext_temp_EXE),
	.f(f),
	.C(C)
);

EXE_MEM EXE_MEM(
	.clk(cpu_clk),
	.rst(cpu_rst),
	.susp({suspend[4], suspend[0]}),
	.rf_we_pre(rf_we_temp_EXE),
	.rf_wsel_pre(rf_wsel_temp_EXE),
	.ram_we_pre(ram_we_temp_EXE),
	.pc4_pre(pc4_temp_EXE),
	.C_pre(C),
	.rD2_pre(rD2_temp_EXE),
	.ext_pre(ext_temp_EXE),
	.wR_pre(wR_temp_EXE),
	.rf_we_next(rf_we_temp_MEM),
	.rf_wsel_next(rf_wsel_temp_MEM),
	.ram_we_next(ram_we_temp_MEM),
	.pc4_next(pc4_temp_MEM),
	.C_next(C_temp_MEM),
	.rD2_next(rD2_temp_MEM),
	.ext_next(ext_temp_MEM),
	.wR_next(wR_temp_MEM)
);

// MEM(bus wires)
assign Bus_addr = C_temp_MEM;
assign Bus_wen = ram_we_temp_MEM;
assign Bus_wdata = rD2_temp_MEM;

MEM_WB MEM_WB(
	.clk(cpu_clk),
	.rst(cpu_rst),
	.rf_we_pre(rf_we_temp_MEM),
	.rf_wsel_pre(rf_wsel_temp_MEM),
	.pc4_pre(pc4_temp_MEM),
	.rdo_pre(Bus_rdata),
	.C_pre(C_temp_MEM),
	.ext_pre(ext_temp_MEM),
	.wR_pre(wR_temp_MEM),
	.rf_we_next(rf_we_temp_WB),
	.rf_wsel_next(rf_wsel_temp_WB),
	.pc4_next(pc4_temp_WB),
	.rdo_next(rdo_temp),
	.C_next(C_temp_WB),
	.ext_next(ext_temp_WB),
	.wR_next(wR_temp_WB)
);

writeBack writeBack (
	.rf_wsel(rf_wsel_temp_WB),
	.pc4(pc4_temp_WB),
	.ext(ext_temp_WB),
	.ALU_C(C_temp_WB),
	.rdo(rdo_temp),
	.wD(wD)
);

data_hazard_detect data_hazard_detect (
	.ID_rRe(ID_rRe),
	.ID_rR1(inst_temp[19:15]),
	.ID_rR2(inst_temp[24:20]),
	.EXE_wR(wR_temp_EXE),
	.EXE_we(rf_we_temp_EXE),
	.MEM_wR(wR_temp_MEM),
	.MEM_we(rf_we_temp_MEM),
	.WB_wR(wR_temp_WB),
	.WB_we(rf_we_temp_WB),
	.suspend(suspend)
);

control_hazard_detect control_hazard_detect (
	.branch(f),
	.flush(flush)
);

`ifdef RUN_TRACE
	reg [2:0] cnt;
	always @ (posedge cpu_clk or posedge cpu_rst) begin
		if (cpu_rst) cnt <= 3'b000;
		else if (cnt != 3'b100) cnt <= cnt + 3'b001;
	end
	
    // Debug Interface
    assign debug_wb_have_inst = (cnt == 3'b100);
    assign debug_wb_pc        = pc4_temp_WB - 4;
    assign debug_wb_ena       = rf_we_temp_WB;
    assign debug_wb_reg       = wR_temp_WB;
    assign debug_wb_value     = wD;
`endif

endmodule
